﻿using System;
using System.ComponentModel;
using System.IO;
using System.Windows.Forms;
using gixoom.Dom;
using gixoom.Dom.Block;
using gixoom.Dom.Score;
using gixoom.Dom.Storage.File;

namespace gixoom.ClientShell
{
    internal partial class ClientShellForm : Form
    {
        private IBlockFacade blockFacade;
        private IScoreFacade scoreFacade;
        private IFileStorage fileStorage;

        private FileStream fileStream;

        /// <summary>
        /// Creates ClientShellForm object.
        /// </summary>
        /// <param name="scoreFacade">score facade</param>
        /// <param name="blockFacade">block facade</param>
        /// <param name="fileStorage">file storage</param>
        /// <exception cref="System.ArgumentNullException">One of the arguments is a null reference.</exception>
        public ClientShellForm(IScoreFacade scoreFacade, IBlockFacade blockFacade,
            IFileStorage fileStorage)
        {
            Ensure.ArgumentNotNull(scoreFacade, "scoreFacade");
            Ensure.ArgumentNotNull(blockFacade, "blockFacade");
            Ensure.ArgumentNotNull(fileStorage, "fileStorage");

            InitializeComponent();
            InitializeComboBoxBlockType();

            this.scoreFacade = scoreFacade;
            this.blockFacade = blockFacade;
            this.fileStorage = fileStorage;
		}

		#region Event Handlers

		private void buttonChooseFile_Click(object sender, EventArgs e)
		{
			chooseFileDialog.FileName = string.Empty;
			var result = chooseFileDialog.ShowDialog();

			if (result == DialogResult.OK)
			{
				textBoxFileName.Text = chooseFileDialog.FileName;
                textBoxScore.Text = null;
                textBoxBlockType.Text = null;
			}

            if (fileStream != null)
            {
                fileStream.Close();
            }

            string message;
            bool canReadFile = FileNamePossiblyChanged(textBoxFileName.Text, out message,
                out fileStream);
            buttonSave.Enabled = canReadFile; 
            labelMessage.Text = message;

            if (canReadFile)
            {
                buttonSave.Focus();
            }
		}

		private void buttonSave_Click(object sender, EventArgs e)
		{
            if (fileStream != null)
            {
                // TODO: SaveFile method can return null. The case should be processed.
                var blockId = fileStorage.SaveFile(fileStream);
                textBoxScore.Text = blockId.Score.ToString();
                textBoxBlockType.Text = blockId.BlockType.ToString();
            }
		}

		private void textBoxFileName_KeyDown(object sender, KeyEventArgs e)
		{
			if (e.KeyData == Keys.Enter)
			{
                if (fileStream != null)
                {
                    fileStream.Close();
                }

                string message;
				bool canReadFile = FileNamePossiblyChanged(textBoxFileName.Text, out message,
                    out fileStream);
                buttonSave.Enabled = canReadFile;
                labelMessage.Text = message;

                if (canReadFile)
                {
                    buttonSave.Focus();
                }
                else
                {
                    textBoxScore.Text = null;
                    textBoxBlockType.Text = null;
                }
			}
		}

		private void textBoxFileName_Leave(object sender, EventArgs e)
		{
            if (fileStream != null)
            {
                fileStream.Close();
            }

            string message;
            bool canReadFile = FileNamePossiblyChanged(textBoxFileName.Text, out message,
                out fileStream);
            buttonSave.Enabled = canReadFile;
            labelMessage.Text = message;

            if (!canReadFile)
            {
                textBoxScore.Text = null;
                textBoxBlockType.Text = null;
            }
		}

        private void textBoxScore_TextChanged(object sender, EventArgs e)
        {
            buttonPasteScoreAndBlockTypeOfSavedFile.Enabled =
                IsButtonPasteScoreAndBlockTypeOfSavedFileEnabled();
        }

        private void textBoxBlockType_TextChanged(object sender, EventArgs e)
        {
            buttonPasteScoreAndBlockTypeOfSavedFile.Enabled =
                IsButtonPasteScoreAndBlockTypeOfSavedFileEnabled();
        }

		private void buttonExtractFile_Click(object sender, EventArgs e)
		{
            string message;
            var blockId = FormBlockId(textBoxScoreExtract.Text,
                comboBoxBlockTypeExtract.SelectedItem as string, out message);

            if (blockId == null)
            {
                // TODO: localize hardcoded string
                MessageBox.Show(message, "Warning!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            else
            {
                string fileName = fileStorage.ExtractFile(blockId);

                if (fileName == null)
                {
                    // TODO: user should be informed that file cannot be extracted.
                    textBoxExtractedFile.Text = null;
                }
                else
                {
                    textBoxExtractedFile.Text = fileName;
                }
            }
		}

        private void buttonPasteScoreAndBlockTypeOfSavedFile_Click(object sender, EventArgs e)
        {
            textBoxScoreExtract.Text = textBoxScore.Text;
            comboBoxBlockTypeExtract.SelectedItem = textBoxBlockType.Text;
        }

		#endregion

		#region Helper Methods

        private void InitializeComboBoxBlockType()
        {
            string[] names = Enum.GetNames(typeof(BlockType));
            comboBoxBlockTypeExtract.Items.AddRange(names);
        }

        private bool IsButtonPasteScoreAndBlockTypeOfSavedFileEnabled()
        {
            bool result = !string.IsNullOrEmpty(textBoxScore.Text) &&
                !string.IsNullOrEmpty(textBoxBlockType.Text);

            return result;
        }

        private IBlockId FormBlockId(string scoreString, string blockTypeString, out string message)
        {
            // TODO: message should be specified more precisely.

            IBlockId blockId = null;
            message = null;

            if (string.IsNullOrEmpty(scoreString))
            {
                // TODO: localize hardcoded string
                message = "There is no score specified. Both score and block type should " +
                    "be specified in order to extract file.";
            }
            else if (string.IsNullOrEmpty(blockTypeString))
            {
                // TODO: localize hardcoded string
                message = "There is no block type specified. Both score and block type should " +
                    "be specified in order to extract file.";
            }
            else
            {
                try
                {
                    var score = scoreFacade.CreateScoreFromString(scoreString);
                    BlockType blockType = (BlockType)Enum.Parse(typeof(BlockType), blockTypeString);
                    blockId = blockFacade.CreateBlockId(blockType, score);
                }
                catch (ArgumentException)
                {
                    // TODO: localize hardcoded string
                    message = "The score string is not valid. Please specify a valid score string.";
                }
            }

            return blockId;
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);

            if (!e.Cancel && fileStream != null)
            {
                fileStream.Close();
            }
        }

        /// <summary>
        /// Returns true if file with the specified name can be read.
        /// </summary>
        /// <param name="fileName">File name.</param>
        /// <param name="message">Message returned in case when the file cannot be read.</param>
        /// <param name="fileStream">FileStream object returned in case when the file can be read.</param>
        /// <returns>True if file can be read.</returns>
		private bool FileNamePossiblyChanged(string fileName, out string message, out FileStream fileStream)
		{
			bool canReadFile = false;
            message = null;
            fileStream = null;

			if (!string.IsNullOrEmpty(fileName))
			{
				bool doesFileExist = File.Exists(fileName);

                if (doesFileExist)
                {
                    fileStream = OpenFile(fileName);

                    if (fileStream == null)
                    {
                        // TODO: localize hardcoded string
                        message = "The file cannot be opened";
                    }
                }
                else
                {
                    // TODO: localize hardcoded string
                    message = "There is no such file";
                }

				canReadFile = (doesFileExist && fileStream != null);
			}

			return canReadFile;
		}

        private FileStream OpenFile(string name)
        {
            FileStream result = null;

            // TODO: exceptions should be properly processed
            try
            {
                result = File.OpenRead(name);
            }
            catch
            {
            }

            return result;
        }

        #endregion
    }
}
